package main

import (
	"flag"
	"fmt"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/alidns"
	"log"
	"os"
	"time"
)

var (
	configuration Settings
	optConf       = flag.String("c", "./config.json", "Specify a config file")
	Version       = "0.1"
)

func main() {
	flag.Parse()
	err := LoadSettings(*optConf, &configuration)
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}
	_ = CheckSettings(&configuration)

	log.SetPrefix("[MyAliDDNS] ")
	log.Println("MyAliDDNS started, entering main loop...")
	dnsLoop()
}

func dnsLoop() {
	domains := configuration.Domains
	for _, domain := range domains {
		DomainLoop(domain)
	}
}

func DomainLoop(domain Domain) {
	domainName := domain.DomainName
	var lastIpv4 = ""
	var lastIpv6 = ""
	for {
		iPv4, err := GetCurrentIPv4()
		iPv6, err1 := GetCurrentIPv6()
		if err != nil && err1 != nil {
			time.Sleep(time.Second * time.Duration(configuration.Interval))
			continue
		}
		log.Printf("current IPv4 is: %s, current IPv6 is: %s", iPv4, iPv6)
		if iPv4 != "" && iPv4 != lastIpv4 {
			lastIpv4 = iPv4
			for _, subDomain := range domain.SubDomains {
				str := subDomain + "." + domainName
				_ = UpdateIp(str, lastIpv4, Ipv4Type)
			}
		}
		if iPv6 != "" && iPv6 != lastIpv6 {
			lastIpv6 = iPv6
			for _, subDomain := range domain.SubDomains {
				str := subDomain + "." + domainName
				_ = UpdateIp(str, lastIpv6, Ipv6Type)
			}
		}
		log.Printf("Going to sleep, will start next checking in %d seconds...\r\n", configuration.Interval)
		time.Sleep(time.Second * time.Duration(configuration.Interval))
	}
}

func UpdateIp(subDomainName string, lastIp string, recordType string) error {
	myRecord, err := DescribeSubDomainRecords(subDomainName, recordType)
	if err != nil {
		log.Printf("Cannot get subdomain %s from AliDNS.\r\n", subDomainName)
		return err
	}
	for _, v := range myRecord {
		if v.Value == lastIp {
			log.Printf("%s is the same as cached one. Skip update.\n", lastIp)
		} else {
			v.Value = lastIp
			err := UpdateDomainRecord(v)
			if err != nil {
				log.Printf("Failed to update IP for subdomain: %s\r\n", subDomainName)
				return err
			} else {
				log.Printf("IP updated for subdomain: %s %s\r\n", subDomainName, lastIp)
			}
		}
	}
	return nil
}

func DescribeSubDomainRecords(subDomain string, recordType string) ([]alidns.Record, error) {
	client, err := alidns.NewClientWithAccessKey("cn-hangzhou", configuration.Email, configuration.Password)
	request := alidns.CreateDescribeSubDomainRecordsRequest()
	request.Scheme = "https"

	request.SubDomain = subDomain
	request.Type = recordType
	response, err := client.DescribeSubDomainRecords(request)
	var myRecord []alidns.Record
	if err != nil {
		fmt.Print(err.Error())
		return myRecord, err
	}
	myRecord = response.DomainRecords.Record
	return myRecord, nil
}

func UpdateDomainRecord(myRecord alidns.Record) error {
	client, err := alidns.NewClientWithAccessKey("cn-hangzhou", configuration.Email, configuration.Password)
	request := alidns.CreateUpdateDomainRecordRequest()
	request.Scheme = "https"

	request.RecordId = myRecord.RecordId
	request.RR = myRecord.RR
	request.Type = myRecord.Type
	request.Value = myRecord.Value
	_, err = client.UpdateDomainRecord(request)
	if err != nil {
		fmt.Printf("UpdateDomainRecord error.%+v\n", err)
		return err
	}
	return err
}
